/*
 * Fupro.java
 *
 * Created on August 21, 2007, 6:00 PM
 *
 */

package org.eruta.tool;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigInteger;
import java.math.BigDecimal;
/**
 *
 * @author bjorn
 */
public class Fupro {
    public static final class Function {
        private    
        String      _name;
        private
        Class<?>    _class;
        private
        Object      _object;
        private
        Class<?>[]  _paramtype;  
        private
        Method      _method;
        public    
        Function(String name, Class<?> klass, Object objekt, Class<?> ... paramtype) {
            _name   = name;
            _class  = klass;
            _object = objekt;
            _paramtype = paramtype;
            _method    = null;
            try {
               _method = _class.getMethod(_name, _paramtype);
            } catch (SecurityException ex) {
                ex.printStackTrace();
            } catch (NoSuchMethodException ex) {
                ex.printStackTrace();
            }

        }
        
        public Object call( Object... arguments ) {
            if(_method == null) return null;
            try {
                    return _method.invoke(_object, arguments);
            } catch (IllegalArgumentException ex) {
                    ex.printStackTrace();
            } catch (InvocationTargetException ex) {
                    ex.printStackTrace();
            } catch (IllegalAccessException ex) {
                    ex.printStackTrace();
            }
            return null;
        }
        
    }
    
        
    public static class Value {
    private String          _string     = null;
    private BigDecimal      _number     = null;
    private Fupro.Function  _function   = null;
    private boolean         _nil        = false;
    /** Constructors. */
    public Value(String s)          { _string   = s ;                       }
    public Value(long i)            { _number   = BigDecimal.valueOf(i);    }
    public Value(double d)          { _number   = BigDecimal.valueOf(d);    }
    public Value(Fupro.Function fu) { _function = fu;                       }
    public Value()                  { _nil      = true;                     }
    
    public static Value v(String s)        { return new Value(s); }
    public static Value v(long i)          { return new Value(i); }
    public static Value v(double d)        { return new Value(d); }
    public static Value v(Fupro.Function f){ return new Value(f); }
    public static Value v()                { return new Value();  }
    /** Getters. We don't need setters as this class uses pure referencing. */
    public String         toString()   { return _string ;               }
    public long           toLong()     { return _number.longValue();    }
    public BigDecimal     toNumber()   { return _number;                }
    public double         toDouble()   { return _number.doubleValue();  } 
    public Fupro.Function toFunction() { return _function;              }
    public boolean        isFunction() { return _function   != null;    }
    // public boolean        isList()     { return _list       != null;    }
    public boolean        isString()   { return _string     != null;    }
    public boolean        isNumber()   { return _number     != null;    }
    public boolean        isNil()      { return _nil;                   } 
    }
       
    
    public static class List extends Value {
        Value _head;
        List _tail;
        
        public List() {
            _head = new Value();
            _tail = null;
        }
        
        public List(Value head) {
            _head = head;
            _tail = new List();
        }
        
        public List(Value head, List tail) {
            _head = head;
            _tail = tail;            
        }
        
        public Value getHead() {
             return _head;   
        }
        
        public List getTail() {
             return _tail;   
        }
        
        public Value car() {
            if(this._head == null) return new Value();
            return this._head;
        }
              
        public List cdr() {
            if(this._tail == null) return new List();
            return this._tail;        
        }
        
        public static List cons(Value head, List tail) {
            return new List(head, tail);
        }
        
        public static List cons(Value head, Value tail) {
            return List.cons(head, new List(tail)) ;
        }
        
        public static List make_empty() {
            return new List();
        }
        
        public static List make_single(Value value) {
            return new List(value);
        }
        
        public static Value car(List list) {
            return list.car();
        }
        
        public static List cdr(List list) {
            return list.cdr();
        }

        public Fupro.Value get_head() {
            return _head;
        }
        
        public static List l(Value ... elements) {
            List result = make_empty();    
            int stop = elements.length - 1;
            for(int i = stop; i >= 0; i--) {                
                Value element = elements[i];
                List aid;
                if(element.getClass() == Fupro.List.class) {
                    aid = cons((List) element, result);
                } else {
                    aid = cons(element, result);
                }
              
                result   = aid;
            }
            return result;
        }
        
    }
    
    
    /** Creates a new instance of Fupro */
    public Fupro() {
    }
    
   /* public static void main(String arg[]) {
        List list1 = List.l(Value.v("Hello"), List.l(Value.v("World"), Value.v("!") ) );
        System.out.format("%s ", list1.car().toString());
        System.out.format("%s ", list1.cdr().car().toString());
        System.out.format("%s\n", list1.cdr().cdr().car().toString());
    
    }*/
    
}
